package com.atXiao.taste.recommand;

import java.util.*;
import java.util.stream.Collectors;

public class CollaborativeFilteringRecommendation {

    // 用户-美食评分表，实际中可能从数据库中获取
    private static Map<String, Map<String, Double>> userRatings = new HashMap<>();

    public static void main(String[] args) {
        // 示例数据
        // 示例数据
        userRatings.put("User1", createImmutableMap("Food1", 5.0, "Food2", 4.0, "Food3", 3.0));
        userRatings.put("User2", createImmutableMap("Food1", 4.0, "Food2", 5.0, "Food3", 2.0));
        userRatings.put("User3", createImmutableMap("Food1", 3.0, "Food2", 2.0, "Food3", 4.0));

        // 为 User4 添加评分记录
        userRatings.put("User4", createImmutableMap("Food1", 5.0, "Food2", 2.0, "Food3", 3.0));

        String targetUser = "User4";

        if (userRatings.containsKey(targetUser)) {
            List<String> recommendedFoods = recommendFoods(targetUser);
            System.out.println("Recommended Foods for " + targetUser + ": " + recommendedFoods);
        } else {
            System.out.println("No ratings found for target user: " + targetUser);
        }
    }

    private static Map<String, Double> createImmutableMap(Object... keyValuePairs) {
        Map<String, Double> map = new HashMap<>();
        for (int i = 0; i < keyValuePairs.length; i += 2) {
            map.put((String) keyValuePairs[i], (Double) keyValuePairs[i + 1]);
        }
        return Collections.unmodifiableMap(map);
    }

    // 基于协同过滤推荐美食
    public static List<String> recommendFoods(String targetUser) {
        // 计算用户相似度
        Map<String, Double> userSimilarities = calculateUserSimilarities(targetUser);

        // 获取相似用户集合
        List<String> similarUsers = getSimilarUsers(userSimilarities);

        // 生成候选集
        List<String> candidateFoods = generateCandidateFoods(similarUsers);

        // 过滤已经尝试过的美食
        candidateFoods.removeAll(userRatings.get(targetUser).keySet());

        // 按评分高低排序，选择前几个进行推荐
        candidateFoods.sort(Comparator.comparingDouble(food -> calculateWeightedScore((String) food, userSimilarities, targetUser))
                .reversed());

        return candidateFoods.subList(0, Math.min(5, candidateFoods.size())); // 最多推荐5个美食
    }

    // 计算用户相似度
    private static Map<String, Double> calculateUserSimilarities(String targetUser) {
        Map<String, Double> userSimilarities = new HashMap<>();
        for (String user : userRatings.keySet()) {
            if (!user.equals(targetUser)) {
                double similarity = calculateCosineSimilarity(userRatings.get(targetUser), userRatings.get(user));
                userSimilarities.put(user, similarity);
            }
        }
        return userSimilarities;
    }

    // 计算余弦相似度
    private static double calculateCosineSimilarity(Map<String, Double> ratings1, Map<String, Double> ratings2) {
        Set<String> commonFoods = new HashSet<>(ratings1.keySet());
        commonFoods.retainAll(ratings2.keySet());

        if (commonFoods.isEmpty()) {
            return 0.0;  // No common items, similarity is zero.
        }

        double dotProduct = 0.0;
        double magnitude1 = 0.0;
        double magnitude2 = 0.0;

        for (String food : commonFoods) {
            double rating1 = ratings1.get(food);
            double rating2 = ratings2.get(food);

            dotProduct += rating1 * rating2;
            magnitude1 += Math.pow(rating1, 2);
            magnitude2 += Math.pow(rating2, 2);
        }

        magnitude1 = Math.sqrt(magnitude1);
        magnitude2 = Math.sqrt(magnitude2);

        return dotProduct / (magnitude1 * magnitude2);
    }


    // 获取相似用户集合
    private static List<String> getSimilarUsers(Map<String, Double> userSimilarities) {
        // 根据相似度排序，选择相似度高的用户
        return userSimilarities.entrySet().stream()
                .sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))
                .limit(3) // 选择前3个相似用户
                .map(Map.Entry::getKey)
                .collect(Collectors.toList());
    }


    // 生成候选集
    private static List<String> generateCandidateFoods(List<String> similarUsers) {
        List<String> candidateFoods = new ArrayList<>();
        for (String user : similarUsers) {
            candidateFoods.addAll(userRatings.get(user).keySet());
        }
        return new ArrayList<>(new HashSet<>(candidateFoods)); // 去重
    }

    // 计算加权评分
    private static double calculateWeightedScore(String food, Map<String, Double> userSimilarities, String targetUser) {
        double weightedScore = 0.0;
        double similaritySum = 0.0;

        for (String user : userSimilarities.keySet()) {
            if (userRatings.get(user).containsKey(food)) {
                double similarity = userSimilarities.get(user);
                weightedScore += similarity * userRatings.get(user).get(food);
                similaritySum += Math.abs(similarity);
            }
        }

        if (similaritySum > 0) {
            return weightedScore / similaritySum;
        } else {
            return 0.0;
        }
    }
}
